src: release context frame in AsyncWrap::EmitDestroy#61995
src: release context frame in AsyncWrap::EmitDestroy#61995Flarna wants to merge 4 commits intonodejs:mainfrom
Conversation
Release the async context frame in AsyncWrap::EmitDestroy to allow gc to collect it. This is in special relevant for reused resources like HTTPParser otherwise they might keep ALS stores alive.
391ae5a to
bdd2d38
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #61995 +/- ##
==========================================
- Coverage 89.77% 89.64% -0.13%
==========================================
Files 674 676 +2
Lines 205705 206252 +547
Branches 39449 39511 +62
==========================================
+ Hits 184670 184897 +227
- Misses 13280 13476 +196
- Partials 7755 7879 +124
🚀 New features to boost your workflow:
|
| HandleScope handle_scope(env()->isolate()); | ||
| USE(object()->Set(env()->context(), env()->resource_symbol(), object())); | ||
| } | ||
| context_frame_.Reset(); |
There was a problem hiding this comment.
Not for this PR, but do you know how it comes this is a v8::Global to begin with and not an internal field on object()?
There was a problem hiding this comment.
No particular reason, I guess. Probably could be an internal field. Just needed to have a context frame reference somewhere related to an AsyncWrap so it could do the restore between the before and after. It was just easier to store directly on the AsyncWrap since the point it captures at was already on the native side.
Co-authored-by: Anna Henningsen <github@addaleax.net>
There was a problem hiding this comment.
Sorry, just saw your comments on the DCHECK (hidden because of the resolved comment) – that's exactly why the DCHECK should be in place, we should not be able to MakeCallback() when there isn't a corresponding context frame to be entered.
I can try to find some time to dig into those failures.
This comment was marked as outdated.
This comment was marked as outdated.
|
Yeah, the only error I'm getting is this one: That's the test from #37958, which was added to complete coverage and intentionally uses internal APIs. We could skip the DCHECK for now, but it does catch a valid issue here I'd say. |
|
Setting The failing test shows actually a race in HTTP:
Adding But well, thats an unrelated issue for an http expert. Do you want me to remove the |
No. We should have something that tells us if the behavior is broken that way. c458399 only fixed the test by breaking the I'm kind of leaning towards something like #ifdef DEBUG
if (
context_frame_.IsEmpty() ||
context_frame_.Get(env()->isolate())->IsUndefined()
) {
ProcessEmitWarning(env(), "MakeCallback() called without context frame");
}
#endifwith the goal of eventually turning it into a regular |
|
I don't think emitting a warning in case
The ALS implementation is handling We could move towards setting some What about adding following and reverting c458399 ? |
Yeah, that would work then 👍 |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Commit Queue failed- Loading data for nodejs/node/pull/61995 ✔ Done loading data for nodejs/node/pull/61995 ----------------------------------- PR info ------------------------------------ Title src: release context frame in AsyncWrap::EmitDestroy (#61995) ⚠ Could not retrieve the email or name of the PR author's from user's GitHub profile! Branch Flarna:als-http-parser-leak -> nodejs:main Labels c++, author ready, needs-ci, commit-queue-squash, dont-land-on-v20.x, async_local_storage Commits 4 - src: release context frame in AsyncWrap::EmitDestroy - Add DCHECK to ensure context_frame is not emtpy in MakeCallback - use undefined to avoid empty global - set empty context_frame_, issue a process warning Committers 2 - Gerhard Stöbich <18708370+Flarna@users.noreply.github.com> - GitHub <noreply@github.com> PR-URL: https://github.com/nodejs/node/pull/61995 Fixes: https://github.com/nodejs/node/issues/61882 Refs: https://github.com/nodejs/node/pull/48528 Reviewed-By: Anna Henningsen <anna@addaleax.net> ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/61995 Fixes: https://github.com/nodejs/node/issues/61882 Refs: https://github.com/nodejs/node/pull/48528 Reviewed-By: Anna Henningsen <anna@addaleax.net> -------------------------------------------------------------------------------- ℹ This PR was created on Thu, 26 Feb 2026 00:07:32 GMT ✔ Approvals: 1 ✔ - Anna Henningsen (@addaleax): https://github.com/nodejs/node/pull/61995#pullrequestreview-3879134622 ✘ This PR needs to wait 42 more hours to land (or 0 minutes if there is one more approval) ✔ Last GitHub CI successful ℹ Last Full PR CI on 2026-03-03T05:16:27Z: https://ci.nodejs.org/job/node-test-pull-request/71538/ - Querying data for job/node-test-pull-request/71538/ ✔ Build data downloaded ✔ Last Jenkins CI successful -------------------------------------------------------------------------------- ✔ Aborted `git node land` session in /home/runner/work/node/node/.ncuhttps://github.com/nodejs/node/actions/runs/22611759900 |
Qard
left a comment
There was a problem hiding this comment.
LGTM. As for the note about v8::Global versus internal field: there's no particular reason it is the way it is. Just seemed more straightforward at the time since the frame value is initially captured from the native side. Simply adding it to the AsyncWrap rather than the wrapped JS object itself seemed the most straightforward thing to do at the time.
| HandleScope handle_scope(env()->isolate()); | ||
| USE(object()->Set(env()->context(), env()->resource_symbol(), object())); | ||
| } | ||
| context_frame_.Reset(); |
There was a problem hiding this comment.
No particular reason, I guess. Probably could be an internal field. Just needed to have a context frame reference somewhere related to an AsyncWrap so it could do the restore between the before and after. It was just easier to store directly on the AsyncWrap since the point it captures at was already on the native side.
Release the async context frame in
AsyncWrap::EmitDestroyto allow gc to collect it.This is in special relevant for reused resources like
HTTPParserotherwise they might keep ALS stores alive.Fixes: #61882
Refs: #48528